package mxmf.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.CollectionUtils;

import javassist.tools.rmi.ObjectNotFoundException;

public class BeanUtilsHelper {

    private static final Logger logger = LoggerFactory.getLogger(BeanUtilsHelper.class);
    private Converter<String, Date> dateConverter;

    private BeanUtilsHelper(Converter<String, Date> dateConverter) {
        this.dateConverter = dateConverter;
    }

    public static BeanUtilsHelper instance(Converter<String, Date> dateConverter) {
        return new BeanUtilsHelper(dateConverter);
    }

    public static BeanUtilsHelper getInstance(String yyyy) {
        Converter<String, Date> converter = new Converter<String, Date>() {
            @Override
            public Date convert(String source) {
                DateFormat sdf = new SimpleDateFormat(yyyy);
                try {
                    return sdf.parse(source);
                } catch (ParseException e) {
                    logger.error(e.toString(), e);
                    throw new RuntimeException(e);
                }
            }
        };
        // sdf = new SimpleDateFormat(yyyy);
        //
        // Converter<String, Date> converter = BeanUtilsHelper::convert;
        return new BeanUtilsHelper(converter);
    }

    /**
     * 拷贝实体，source,target不允许为空
     *
     * @param source
     * @param target
     */
    public void copyProperties(Object source, Object target) {
        BeanUtils.copyProperties(source, target);
    }

    /**
     * 拷贝实体集合，sourceList
     *只支持自定义实体集合拷贝 
     *应用场景：DTO <=> DO 等
     * @throws IllegalAccessException 
     * @throws InstantiationException 
     * @throws ObjectNotFoundException 
     */
    public void copyPropertiesList(List sourceList, List targetList, Class clazz)
            throws InstantiationException, IllegalAccessException, ObjectNotFoundException {
        if (CollectionUtils.isEmpty(sourceList)) {
            throw new ObjectNotFoundException("CommonErrorEnum.PARAM_ERROR");
        }
        for (Object items : sourceList) {
            Object target = clazz.newInstance();
            BeanUtils.copyProperties(items, target);
            targetList.add(target);
        }

    }

    /**
     * Map --> Bean 1: 利用Introspector,PropertyDescriptor实现 Map --> Bean
     *
     * http://takeme.iteye.com/blog/2357777
     * @param map
     * @param obj
     */
    public void transMap2Bean(Map<String, Object> map, Object obj) throws Exception {

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (map.containsKey(key)) {
                Object value = map.get(key);
                // 得到property对应的setter方法
                Method setter = property.getWriteMethod();
                Class<?> type = property.getPropertyType();
                if (value instanceof Map) {
                    Map subMap = (Map) value;
                    Object object = type.newInstance();
                    transMap2Bean(subMap, object);
                    setter.invoke(obj, object);
                    continue;
                } else if (value instanceof List) {
                    ParameterizedType pt = (ParameterizedType) property.getReadMethod()
                            .getGenericReturnType();
                    Class genericType = (Class) pt.getActualTypeArguments()[0];
                    List result = creteMap2Bean((List) value, genericType);

                    setter.invoke(obj, result);
                    continue;

                }
                if (Double.class.equals(type)) {
                    setter.invoke(obj, Double.valueOf(value.toString()));
                    continue;

                } else if (Date.class.equals(type)) {
                    if (value instanceof Number) {
                        long time = ((Number) value).longValue();
                        setter.invoke(obj, new Date(time));
                    } else if (value instanceof String) {
                        Date date = this.dateConverter.convert((String) value);
                        setter.invoke(obj, date);
                    }
                    continue;
                } else if (value != null && type.equals(Long.class) && value instanceof Integer) {
                    setter.invoke(obj, Long.valueOf(value.toString()));
                    continue;
                }
                setter.invoke(obj, value);
            }
        }
    }

    /**
     * Bean --> Map 1: 利用Introspector和PropertyDescriptor 将Bean --> Map
     *
     * @param obj
     */
    public Map<String, Object> transBean2Map(Object obj)
            throws IntrospectionException, InvocationTargetException, IllegalAccessException {

        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();

            // 过滤class属性
            if (!key.equals("class")) {
                // 得到property对应的getter方法
                Method getter = property.getReadMethod();
                Object value = getter.invoke(obj);

                map.put(key, value);
            }

        }
        return map;
    }

    public List creteMap2Bean(List<Map<String, Object>> items, Class clazz) throws Exception {
        List result = new ArrayList();
        if (items == null) {
            return result;
        }
        for (Map<String, Object> map : items) {
            Object object = clazz.newInstance();
            this.transMap2Bean(map, object);
            result.add(object);
        }
        return result;
    }

}
